/*FreeMind - A Program for creating and viewing Mindmaps
*Copyright (C) 2000-2006 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitri Polivaev and others.
*
*See COPYING for Details
*
*This program is free software; you can redistribute it and/or
*modify it under the terms of the GNU General Public License
*as published by the Free Software Foundation; either version 2
*of the License, or (at your option) any later version.
*
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*GNU General Public License for more details.
*
*You should have received a copy of the GNU General Public License
*along with this program; if not, write to the Free Software
*Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
/*
 * Created on 10.12.2005
 * Copyright (C) 2005 Dimitri Polivaev
 */
package freemind.modes.mindmapmode.attributeactors;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import javax.swing.AbstractListModel;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JRadioButton;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

import freemind.controller.MapModuleManager.MapModuleChangeObserver;
import freemind.main.Resources;
import freemind.main.Tools;
import freemind.modes.MindMap;
import freemind.modes.MindMapNode;
import freemind.modes.Mode;
import freemind.modes.attributes.Attribute;
import freemind.modes.attributes.AttributeRegistry;
import freemind.modes.attributes.AttributeRegistryElement;
import freemind.modes.attributes.NodeAttributeTableModel;
import freemind.modes.mindmapmode.MindMapMode;
import freemind.view.MapModule;
import freemind.view.mindmapview.MapView;
import freemind.view.mindmapview.NodeView;

public class AssignAttributeDialog extends JDialog implements AttributesListener, MapModuleChangeObserver{
    private static class ClonedComboBoxModel extends AbstractListModel implements ComboBoxModel{
        private AbstractListModel sharedListModel;
        private Object selectedItem;
        private ListDataEvent ev = null; 
        public ClonedComboBoxModel(ComboBoxModel sharedListModel) {
            super();
            this.sharedListModel = (AbstractListModel)sharedListModel;
        }
        
        public void addListDataListener(ListDataListener l) {
            super.addListDataListener(l);
            sharedListModel.addListDataListener(l);
        }
        
        public Object getElementAt(int index) {
            return sharedListModel.getElementAt(index);
        }
        
        public int getSize() {
            return sharedListModel.getSize();
        }
        
        public void removeListDataListener(ListDataListener l) {
            super.removeListDataListener(l);
            sharedListModel.removeListDataListener(l);
        }
        
        public void setSelectedItem(Object anItem) {
            selectedItem = anItem;
            fireContentsChanged(this, -1, -1);
        }

        private ListDataEvent getContentChangedEvent() {
            if(ev == null){
                ev = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, -1, -1);                
            }
            return ev;
        }
        
        public Object getSelectedItem() {
            return selectedItem;
        }
    }
    
    private abstract class IteratingAction implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            try{
				if(selectedBtn.getModel().isSelected()){
                    LinkedList selecteds = mapView.getSelecteds();
                    ListIterator iterator = selecteds.listIterator();
                    while(iterator.hasNext()){
                        NodeView selectedNodeView = (NodeView) iterator.next();
                        performAction(selectedNodeView);
                    }
                    return;
                }
                final NodeView nodeView = mapView.getRoot();
                iterate(nodeView);
            }
            catch(NullPointerException ex){
                
            }
        }
        private void iterate(final NodeView nodeView) {
        	int n = nodeView.getComponentCount();
        	if(nodeView.isVisible()){
        		performAction(nodeView);
        	}
        	for(int i = 0; i < n; i++){
        		Component component = nodeView.getComponent(i);
        		if(component instanceof NodeView){
        			iterate((NodeView)component);
        		}
        	}
        }

        private void performAction(NodeView selectedNodeView) {
            if(! selectedNodeView.isRoot() || ! skipRootBtn.isSelected())
            performAction(selectedNodeView.getModel());
        }
       abstract protected void performAction(MindMapNode model) ;
       
       protected void showEmptyStringErrorMessage() {
           JOptionPane.showMessageDialog(AssignAttributeDialog.this, 
                   Resources.getInstance().getResourceString("attributes_adding_empty_attribute_error"), 
                   Resources.getInstance().getResourceString("error"), JOptionPane.ERROR_MESSAGE);
       }        
    }
    
    private  class AddAction extends IteratingAction{
        private String name;
        private String value;

        protected void performAction(MindMapNode model) {
        	model.createAttributeTableModel();
            NodeAttributeTableModel attributes = model.getAttributes();                         
            attributes.getAttributeController().performInsertRow(attributes, attributes.getRowCount(), name, value);
       }

        public void actionPerformed(ActionEvent e) {
            if(attributeNames.getSelectedItem() == null){
                showEmptyStringErrorMessage();
                return;
            }
            
            name = attributeNames.getSelectedItem().toString();
            if(name.equals("")){
                showEmptyStringErrorMessage();
                return;
            }
            Object valueSelectedItem = attributeValues.getSelectedItem();
            value =  valueSelectedItem != null ? valueSelectedItem.toString() : "";
            
            super.actionPerformed(e);
            if(valueSelectedItem == null){                
                selectedAttributeChanged(name, attributeValues);
            }
        }

        
    }
    
    private  class DeleteAttributeAction extends IteratingAction{
        private String name;
        public void actionPerformed(ActionEvent e) {
            final Object selectedItem = attributeNames.getSelectedItem();
            if(selectedItem == null){
                showEmptyStringErrorMessage();
                return;
            }
            name = selectedItem.toString();
            if(name.equals("")){
                showEmptyStringErrorMessage();
                return;
            }
            super.actionPerformed(e);
        }        
        protected void performAction(MindMapNode model) {
            NodeAttributeTableModel attributes = model.getAttributes();
            for(int i = attributes.getRowCount()-1; i >= 0; i--){
                if(attributes.getAttribute(i).getName().equals(name)){
                    attributes.getAttributeController().performRemoveRow(attributes, i);
                }
            }
       }        
    }
    
    private  class DeleteValueAction extends IteratingAction{
        private String name;
        private String value;
        public void actionPerformed(ActionEvent e) {
            if(attributeNames.getSelectedItem() == null){
                showEmptyStringErrorMessage();
                return;
            }
            
            name = attributeNames.getSelectedItem().toString();
            if(name.equals("")){
                showEmptyStringErrorMessage();
                return;
            }
            Object valueSelectedItem = attributeValues.getSelectedItem();
            value =  valueSelectedItem != null ? valueSelectedItem.toString() : "";
            
            super.actionPerformed(e);
        }        
        protected void performAction(MindMapNode model) {
            NodeAttributeTableModel attributes = model.getAttributes();
            for(int i = attributes.getRowCount()-1; i >= 0; i--){
                Attribute attribute = attributes.getAttribute(i);
                if(attribute.getName().equals(name)
                        && attribute.getValue().equals(value)){
                    attributes.getAttributeController().performRemoveRow(attributes, i);
                }
            }
       }        
    }

    private  class ReplaceValueAction extends IteratingAction{
        private String name;
        private String value;
        private String replacingName;
        private String replacingValue;
        public void actionPerformed(ActionEvent e) {
            if(attributeNames.getSelectedItem() == null){
                showEmptyStringErrorMessage();
                return;
            }
            if(replacingAttributeNames.getSelectedItem() == null){
                showEmptyStringErrorMessage();
                return;
            }
            name = attributeNames.getSelectedItem().toString();
            if(name.equals("")){
                showEmptyStringErrorMessage();
                return;
            }
            replacingName = replacingAttributeNames.getSelectedItem().toString();
            if(replacingName.equals("")){
                showEmptyStringErrorMessage();
                return;
            }
            Object valueSelectedItem = attributeValues.getSelectedItem();
            value =  valueSelectedItem != null ? valueSelectedItem.toString() : "";            
            Object replacingValueSelectedItem = replacingAttributeValues.getSelectedItem();
            replacingValue = replacingValueSelectedItem != null ? replacingValueSelectedItem.toString() :"";
            super.actionPerformed(e);
        }        
        protected void performAction(MindMapNode model) {
            NodeAttributeTableModel attributes = model.getAttributes();
            for(int i = attributes.getRowCount()-1; i >= 0; i--){
                Attribute attribute = attributes.getAttribute(i);
                if(attribute.getName().equals(name)
                        && attribute.getValue().equals(value)){
                    attributes.getAttributeController().performRemoveRow(attributes, i);
                    attributes.insertRow(i, replacingName, replacingValue);
                }
            }
       }        
    }
    
    private static final Dimension maxButtonDimension = new Dimension(1000, 1000);
    
    private MapView mapView;
    private JComboBox attributeNames;
    private JComboBox attributeValues;
    private JComboBox replacingAttributeNames;
    private JComboBox replacingAttributeValues;

    private JRadioButton selectedBtn;

    private JRadioButton visibleBtn;

    private JCheckBox skipRootBtn;

    public AssignAttributeDialog(MapView mapView){
        super(JOptionPane.getFrameForComponent(mapView), 
        		Tools.removeMnemonic(Resources.getInstance().getResourceString("attributes_assign_dialog")), false);
        
        final Border actionBorder = new MatteBorder(2, 2, 2, 2, Color.BLACK);
        final Border emptyBorder = new EmptyBorder(5, 5, 5, 5);
        final Border btnBorder = new EmptyBorder(2, 2, 2, 2);
        
        selectedBtn = new JRadioButton();
        Tools.setLabelAndMnemonic(selectedBtn, Resources.getInstance().getResourceString("attributes_for_selected"));
        selectedBtn.setSelected(true);
        visibleBtn = new JRadioButton();
        Tools.setLabelAndMnemonic(visibleBtn, Resources.getInstance().getResourceString("attributes_for_visible"));
        final ButtonGroup group = new ButtonGroup();
        group.add(selectedBtn);
        group.add(visibleBtn);
        
        skipRootBtn = new JCheckBox();
        Tools.setLabelAndMnemonic(skipRootBtn, Resources.getInstance().getResourceString("attributes_skip_root"));
        skipRootBtn.setSelected(true);
        
        final Box selectionBox = Box.createHorizontalBox();
        selectionBox.setBorder(emptyBorder);
        selectionBox.add(Box.createHorizontalGlue());
        selectionBox.add(selectedBtn);
        selectionBox.add(Box.createHorizontalGlue());
        selectionBox.add(visibleBtn);
        selectionBox.add(Box.createHorizontalGlue());
        selectionBox.add(skipRootBtn);
        selectionBox.add(Box.createHorizontalGlue());
        
        getContentPane().add(selectionBox, BorderLayout.NORTH);
        
        final JButton addBtn = new JButton();
        Tools.setLabelAndMnemonic(addBtn, Resources.getInstance().getResourceString("filter_add"));
        addBtn.addActionListener(new AddAction());
        addBtn.setMaximumSize(maxButtonDimension);
        
        JButton deleteAttributeBtn = new JButton();
        Tools.setLabelAndMnemonic(deleteAttributeBtn, Resources.getInstance().getResourceString("attribute_delete"));
        deleteAttributeBtn.addActionListener(new DeleteAttributeAction());
        deleteAttributeBtn.setMaximumSize(maxButtonDimension);
        
        JButton deleteAttributeValueBtn = new JButton();
        Tools.setLabelAndMnemonic(deleteAttributeValueBtn, Resources.getInstance().getResourceString("attribute_delete_value"));
        deleteAttributeValueBtn.addActionListener(new DeleteValueAction());
        deleteAttributeValueBtn.setMaximumSize(maxButtonDimension);
        
        JButton replaceBtn = new JButton();
        Tools.setLabelAndMnemonic(replaceBtn, Resources.getInstance().getResourceString("attribute_replace"));
        replaceBtn.addActionListener(new ReplaceValueAction());
        replaceBtn.setMaximumSize(maxButtonDimension);
        
        Tools.addEscapeActionToDialog(this);

        final String pattern = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
        final JLabel patternLabel = new JLabel(pattern);
        final Dimension comboBoxMaximumSize = patternLabel.getPreferredSize();
        comboBoxMaximumSize.width += 4;
        comboBoxMaximumSize.height += 4;
        attributeNames = new JComboBox();        
        attributeNames.setMaximumSize(comboBoxMaximumSize);
        attributeNames.setPreferredSize(comboBoxMaximumSize);
        attributeNames.addItemListener(new ItemListener(){
            public void itemStateChanged(ItemEvent e) {
                selectedAttributeChanged(e.getItem(), attributeValues);
            }            
        });
        attributeValues = new JComboBox();
        attributeValues.setMaximumSize(comboBoxMaximumSize);
        attributeValues.setPreferredSize(comboBoxMaximumSize);
        replacingAttributeNames = new JComboBox();
        replacingAttributeNames.setMaximumSize(comboBoxMaximumSize);
        replacingAttributeNames.setPreferredSize(comboBoxMaximumSize);
        replacingAttributeNames.addItemListener(new ItemListener(){
            public void itemStateChanged(ItemEvent e) {
                selectedAttributeChanged(e.getItem(), replacingAttributeValues);
            }            
        });
        replacingAttributeValues = new JComboBox();
        replacingAttributeValues.setMaximumSize(comboBoxMaximumSize);
        replacingAttributeValues.setPreferredSize(comboBoxMaximumSize);
        
        
        final Box addDeleteBtnBox = Box.createVerticalBox();
        addDeleteBtnBox.setBorder(btnBorder);
        addDeleteBtnBox.add(Box.createVerticalGlue());
        addDeleteBtnBox.add(addBtn);
        addDeleteBtnBox.add(deleteAttributeBtn);
        addDeleteBtnBox.add(deleteAttributeValueBtn);
        addDeleteBtnBox.add(Box.createVerticalGlue());
        
        final Box addDeleteBox = Box.createHorizontalBox();
        addDeleteBox.setBorder(actionBorder);
        addDeleteBox.add(Box.createHorizontalGlue());
        addDeleteBox.add(addDeleteBtnBox);
        addDeleteBox.add(Box.createHorizontalStrut(5));
        addDeleteBox.add(attributeNames);
        addDeleteBox.add(Box.createHorizontalStrut(5));
        addDeleteBox.add(attributeValues);
        addDeleteBox.add(Box.createHorizontalStrut(5));
        
        final Box outerReplaceBox = Box.createVerticalBox();
        outerReplaceBox.setBorder(actionBorder);
        
        final Box replaceBox = Box.createHorizontalBox();
        replaceBox.setBorder(btnBorder);
        replaceBox.add(Box.createHorizontalGlue());
        replaceBox.add(replaceBtn);
        replaceBox.add(Box.createHorizontalStrut(5));
        replaceBox.add(replacingAttributeNames);
        replaceBox.add(Box.createHorizontalStrut(5));
        replaceBox.add(replacingAttributeValues);
        replaceBox.add(Box.createHorizontalStrut(5));
        
        outerReplaceBox.add(Box.createVerticalGlue());
        outerReplaceBox.add(replaceBox);
        outerReplaceBox.add(Box.createVerticalGlue());
        
        final Box actionBox = Box.createVerticalBox();
        actionBox.add(Box.createVerticalGlue());
        actionBox.add(addDeleteBox);
        actionBox.add(Box.createVerticalStrut(5));
        actionBox.add(outerReplaceBox);
        actionBox.add(Box.createVerticalGlue());
        getContentPane().add(actionBox, BorderLayout.CENTER);
        
        final JButton closeBtn = new JButton();
        Tools.setLabelAndMnemonic(closeBtn, Resources.getInstance().getResourceString("close"));
        closeBtn.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent e) {
               dispose();
            }
            
        });
        
        final Box bottomBox = Box.createHorizontalBox();
        bottomBox.setBorder(emptyBorder);
        bottomBox.add(Box.createHorizontalGlue());
        bottomBox.add(closeBtn);
        bottomBox.add(Box.createHorizontalGlue());
        
        getContentPane().add(bottomBox, BorderLayout.SOUTH);
        pack();
        comboBoxMaximumSize.width = 1000;
        attributeNames.setMaximumSize(comboBoxMaximumSize);
        attributeValues.setMaximumSize(comboBoxMaximumSize);
        replacingAttributeNames.setMaximumSize(comboBoxMaximumSize);
        replacingAttributeValues.setMaximumSize(comboBoxMaximumSize);
        mapChanged(mapView);
        mapView.getController().getMapModuleManager().addListener(this);
    }
    
    public void mapChanged(MapView currentMapView) {
        if(mapView != null){
            mapView.getModel().getRegistry().getAttributes().removeAttributesListener(this);
        }
        mapView = currentMapView;        
        MindMap map = currentMapView.getModel();
        AttributeRegistry attributes = map.getRegistry().getAttributes();
        attributes.addAttributesListener(this);
        attributesChanged();
    }

    private void selectedAttributeChanged(Object selectedAttributeName, JComboBox values){
        AttributeRegistry attributes = mapView.getModel().getRegistry().getAttributes();
        try{
        AttributeRegistryElement element = attributes.getElement(selectedAttributeName.toString());
        ComboBoxModel selectedValues = element.getValues();
        values.setModel(new ClonedComboBoxModel(selectedValues));
        try{
            Object firstValue = selectedValues.getElementAt(0);
            values.setSelectedItem(firstValue);
        }
        catch(ArrayIndexOutOfBoundsException ex){
        }
        values.setEditable(! element.isRestricted());
        }
        catch(NoSuchElementException ex){
            values.setEditable(! selectedAttributeName.toString().equals(""));  
        }
    }

    public void attributesChanged(ChangeEvent e) {  
        attributesChanged();
    }
    
    private void attributesChanged() {  
        AttributeRegistry attributes = mapView.getModel().getRegistry().getAttributes();
        ComboBoxModel names = attributes.getComboBoxModel();
        attributeNames.setModel(new ClonedComboBoxModel(names));
        attributeNames.setEditable(! attributes.isRestricted());
        replacingAttributeNames.setModel(new ClonedComboBoxModel(names));
        replacingAttributeNames.setEditable(! attributes.isRestricted());
        
        if(attributes.size()> 0){
            Object first =  names.getElementAt(0); 
            attributeNames.setSelectedItem(first);
            replacingAttributeNames.setSelectedItem(first);
            selectedAttributeChanged(attributeNames.getSelectedItem(), attributeValues);
            selectedAttributeChanged( replacingAttributeNames.getSelectedItem(), replacingAttributeValues);
        }
        else{
            attributeValues.setModel(new DefaultComboBoxModel());
            attributeValues.setEditable(false);
            replacingAttributeValues.setModel(new DefaultComboBoxModel());
            replacingAttributeValues.setEditable(false);
        }
    }

    public boolean isMapModuleChangeAllowed(MapModule oldMapModule, Mode oldMode, MapModule newMapModule, Mode newMode) {        
        return ! isVisible() || newMode instanceof MindMapMode;
    }

    public void beforeMapModuleChange(MapModule oldMapModule, Mode oldMode, MapModule newMapModule, Mode newMode) {
    }

    public void afterMapModuleChange(MapModule oldMapModule, Mode oldMode, MapModule newMapModule, Mode newMode) {
        if(newMapModule != null)
        mapChanged(newMapModule.getView());
    }

    public void numberOfOpenMapInformation(int number) {
    }

	public void afterMapClose(MapModule pOldMapModule, Mode pOldMode) {
	}
}